using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;

namespace CompanyX.TechnicalArchitecture.Factories
{
    /// <summary>
    /// Enables defining custom configuration section for provider factory
    /// <code>
    /// &lt;configSections&gt;
    ///   &lt;section name="serviceProviders" type="CompanyX.TechnicalArchitecture.ProviderFactoryConfig, CompanyX.TechnicalArchitecture, PublicKeyToken="1BD8D1813D39DDE3" Version="1.0.0.0" Culture="neutral"/&gt;
    /// &lt;/configSections&gt;
    /// &lt;serviceProviders&gt;
    ///   &lt;providers&gt;
    ///     &lt;clear/&gt;
    ///     &lt;add name="XYZ" assemblyFullName="ABC.XYZ, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1bd8d1813d39dde3"/&gt;
    ///   &lt;/providers&gt;
    /// &lt;/serviceProviders&gt;
    /// </code>
    /// </summary>
    public sealed class ProviderFactoryConfig : ConfigurationSection
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public ProviderFactoryConfig()
        {
        }

        /// <summary>
        /// Provider collection
        /// </summary>
        [ConfigurationProperty("providers", IsDefaultCollection = false)]
        [ConfigurationCollection(typeof(ProviderConfigList), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
        public ProviderConfigList Providers
        {
            get
            {
                return (ProviderConfigList)this["providers"];
            }
            set
            {
                this["providers"] = value;
            }
        }
    }

    /// <summary>
    /// Supports service provider collection
    /// </summary>
    public class ProviderConfigList : ConfigurationElementCollection
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public ProviderConfigList()
        {
            ProviderConfig provider = (ProviderConfig)CreateNewElement();
            Add(provider);
        }

        /// <summary>
        /// Collection type
        /// </summary>
        public override ConfigurationElementCollectionType CollectionType
        {
            get
            {
                return ConfigurationElementCollectionType.AddRemoveClearMap;
            }
        }

        /// <summary>
        /// Creates a new provider configuration element
        /// </summary>
        /// <returns></returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new ProviderConfig();
        }

        /// <summary>
        /// Retrieves the provider configuration element key (name)
        /// </summary>
        /// <param name="element">Provider configuration element to get the key for</param>
        /// <returns>Key value</returns>
        protected override Object GetElementKey(ConfigurationElement element)
        {
            return ((ProviderConfig)element).Name;
        }

        /// <summary>
        /// Accessor for provider configuration element
        /// </summary>
        /// <param name="index">Element to access by index</param>
        /// <returns>Provider configuration element</returns>
        public ProviderConfig this[int index]
        {
            get
            {
                return (ProviderConfig)BaseGet(index);
            }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }

            /// <summary>
            /// Accessor for provider configuration element
            /// </summary>
            /// <param name="Name">Element to access by key name</param>
            /// <returns>Provider configuration element</returns>
        new public ProviderConfig this[string Name]
        {
            get
            {
                return (ProviderConfig)BaseGet(Name);
            }
        }

        /// <summary>
        /// Retrieves the index of the provider configuration element
        /// </summary>
        /// <param name="provider">Provider configuration element</param>
        /// <returns>Index of the element</returns>
        public int IndexOf(ProviderConfig provider)
        {
            return BaseIndexOf(provider);
        }

        /// <summary>
        /// Adds a provider configuration element
        /// </summary>
        /// <param name="provider">Provider configuration element</param>
        public void Add(ProviderConfig provider)
        {
            BaseAdd(provider);
        }
        
        /// <summary>
        /// Wrapper
        /// </summary>
        /// <param name="element"></param>
        protected override void BaseAdd(ConfigurationElement element)
        {
            BaseAdd(element, false);
        }

        /// <summary>
        /// Removes a provider configuration element
        /// </summary>
        /// <param name="provider">Provider configuration element</param>
        public void Remove(ProviderConfig provider)
        {
            if (BaseIndexOf(provider) >= 0)
                BaseRemove(provider.Name);
        }

        /// <summary>
        /// Removes a provider configuration element
        /// </summary>
        /// <param name="index">Element index</param>
        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }

        /// <summary>
        /// Removes a provider configuration element
        /// </summary>
        /// <param name="name">Element name</param>
        public void Remove(string name)
        {
            BaseRemove(name);
        }

        /// <summary>
        /// Clears all provider configuration elements
        /// </summary>
        public void Clear()
        {
            BaseClear();
        }
    }

    /// <summary>
    /// Configuration element for service provider
    /// </summary>
    public sealed class ProviderConfig : ConfigurationElement
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public ProviderConfig()
        {
        }

        /// <summary>
        /// Unique name (alpha-numeric) of the provider
        /// </summary>
        [ConfigurationProperty("name", IsRequired = true)]
        [StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\.,<>?_=+")]
        public string Name
        {
            get { return (string)this["name"]; }
            set { this["name"] = value; }
        }

        /// <summary>
        /// Service provider assembly full-name (must be registered in the GAC or accessible to runtime
        /// via the &lt;codebase&gt; configuration.
        /// </summary>
        [ConfigurationProperty("assemblyFullName", IsRequired = true)]
        public string AssemblyFullName
        {
            get { return (string)this["assemblyFullName"]; }
            set { this["assemblyFullName"] = value; }
        }
    }
}
